﻿// -----------------------------------------------------------------------
// <copyright file="IMG_AdaptiveMedian.cs" company="Hewlett-Packard">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace ImageProcessing.ImageProcessor
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public static class IMG_AdaptiveMedian
    {
        /// <summary>
        /// Adaptives the filter.
        /// </summary>
        /// <param name="orgImg">The org img.</param>
        /// <param name="maxWindowSize">Size of the max window.</param>
        /// <returns>the filtered image</returns>
        public static Entities.IMG_Image AdaptiveFilter(Entities.IMG_Image orgImg, int maxWindowSize)
        {
            try
            {
                int width = orgImg.Width;
                int height = orgImg.Height;
                Entities.IMG_Image retImage = new Entities.IMG_Image(width, height);
                for (int i = 0; i < width; i++)
                {
                    for (int j = 0; j < height; j++)
                    {
                        int window_Size = 3;
                    step1:
                        List<int> window = new List<int>();

                        for (int l = -1 * window_Size / 2; l <= window_Size / 2; l++)
                        {
                            for (int k = -1 * window_Size / 2; k <= window_Size / 2; k++)
                            {
                                int index_W = i + l;
                                int index_H = j + k;
                                int windowIndexX = l + (window_Size / 2);
                                int windowIndexy = k + (window_Size / 2);
                                if (index_H >= 0 && index_H < height && index_W >= 0 && index_W < width)
                                {
                                    window.Add(orgImg[index_H, index_W].BitMixValue);
                                }
                            }
                        }

                        Entities.Pixel zmin = new Entities.Pixel(0, 0, 0);
                        Entities.Pixel zmax = new Entities.Pixel(0, 0, 0);
                        Entities.Pixel zXY = new Entities.Pixel(0, 0, 0);
                        Entities.Pixel zmed = new Entities.Pixel(0, 0, 0);

                        GetMinMaxMed(window, ref zmin, ref zmax, ref zmed);
                        if (zmin.BitMixValue < zmed.BitMixValue && zmed.BitMixValue < zmax.BitMixValue)
                        {
                            goto step2;
                        }
                        else
                        {
                            window_Size += 2;
                            if (window_Size <= maxWindowSize)
                            {
                                goto step1;
                            }
                        }

                    step2:
                        if (zmin.BitMixValue < zXY.BitMixValue && zXY.BitMixValue < zmax.BitMixValue)
                        {
                            retImage[j, i] = new Entities.Pixel(zXY.Red, zXY.Green, zXY.Blue);
                        }
                        else
                        {
                            retImage[j, i] = new Entities.Pixel(zmed.Red, zmed.Green, zmed.Blue);
                        }
                    }
                }

                return retImage;
            }
            catch (Exception img_Ex)
            {
                Logger.LogException(img_Ex);
                throw;
            }
        }

        /// <summary>
        /// Gets the min max med.
        /// </summary>
        /// <param name="window">The window.</param>
        /// <param name="min">The min.</param>
        /// <param name="max">The max.</param>
        /// <param name="med">The med.</param>
        private static void GetMinMaxMed(List<int> window, ref Entities.Pixel min, ref Entities.Pixel max, ref Entities.Pixel med)
        {
            try
            {
                int minValue = window[0];
                int maxValue = window[0];
                int index = window.Count - 1;
                for (int i = 1; i < index; i += 2)
                {
                    if (window[i] < window[i + 1])
                    {
                        if (window[i] < minValue)
                        {
                            minValue = window[i];
                        }

                        if (window[i + 1] > maxValue)
                        {
                            maxValue = window[i + 1];
                        }
                    }
                    else
                    {
                        if (window[i + 1] < minValue)
                        {
                            minValue = window[i + 1];
                        }

                        if (window[i] > maxValue)
                        {
                            maxValue = window[i];
                        }
                    }
                }

                min = GetPixelOfBitminValue(minValue);
                max = GetPixelOfBitminValue(maxValue);
                med = GetPixelOfBitminValue(KthElm(window, 0, window.Count - 1, window.Count / 2));
            }
            catch (Exception img_Ex)
            {
                Logger.LogException(img_Ex);
                throw;
            }
        }

        /// <summary>
        /// Partitions the specified array.
        /// </summary>
        /// <param name="array">The array.</param>
        /// <param name="front">The front.</param>
        /// <param name="rear">The rear.</param>
        /// <returns>partition index</returns>
        private static int Partition(List<int> array, int front, int rear)
        {
            try
            {
                int pivot = array[rear];

                int temp;

                int i = front;

                for (int j = front; j < rear; j++)
                {
                    if (array[j] < pivot)
                    {
                        temp = array[j];
                        array[j] = array[i];
                        array[i++] = temp;
                    }
                }

                temp = array[i];
                array[i] = array[rear];
                array[rear] = temp;
                return i;
            }
            catch (Exception img_Ex)
            {
                Logger.LogException(img_Ex);
                throw;
            }
        }

        /// <summary>
        /// KTHs the elm.
        /// </summary>
        /// <param name="window">The A.</param>
        /// <param name="front">The front.</param>
        /// <param name="rear">The rear.</param>
        /// <param name="i">The i.</param>
        /// <returns>kth elemnt value</returns>
        private static int KthElm(List<int> window, int front, int rear, int i)
        {
            try
            {
                int k;
                if (front == rear)
                {
                    return window[front];
                }

                k = Partition(window, front, rear);

                if (i == k)
                {
                    return window[k];
                }
                else if (i < k)
                {
                    return KthElm(window, front, k - 1, i);
                }
                else
                {
                    return KthElm(window, k + 1, rear, i);
                }
            }
            catch (Exception img_Ex)
            {
                Logger.LogException(img_Ex);
                throw;
            }
        }

        /// <summary>
        /// Gets the pixel of bitmin value. 
        /// </summary>
        /// <param name="returnValue">The return value.</param>
        /// <returns>a generated pixel from the bitmix value</returns>
        private static Entities.Pixel GetPixelOfBitminValue(int returnValue)
        {
            try
            {
                byte red = 0, green = 0, blue = 0;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("1"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("1"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("1"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("2"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("2"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("2"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("4"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("4"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("4"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("8"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("8"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("8"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("16"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("16"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("16"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("32"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("32"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("32"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("64"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("64"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("64"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    blue = (byte)(blue | byte.Parse("128"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    green = (byte)(green | byte.Parse("128"));
                }

                returnValue /= 2;
                if ((returnValue % 2) == 1)
                {
                    red = (byte)(red | byte.Parse("128"));
                }

                return new Entities.Pixel(red, green, blue);
            }
            catch (Exception img_Ex)
            {
                Logger.LogException(img_Ex);
                throw;
            }
        }
    }
}
